package com.dayatang.product.application.impl;


import java.io.File;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Random;
import java.util.Set;

import javax.inject.Inject;

import com.dayatang.domain.AbstractEntity;
import com.dayatang.domain.EntityRepository;
import com.dayatang.domain.InstanceFactory;
import com.dayatang.product.application.ProductApplication;
import com.dayatang.product.domain.Attachment;
import com.dayatang.product.domain.Customer;
import com.dayatang.product.domain.CustomerInfo;
import com.dayatang.product.domain.CustomerVisit;
import com.dayatang.product.domain.DownloadApproval;
import com.dayatang.product.domain.DownloadApprovalCategory;
import com.dayatang.product.domain.DownloadInfo;
import com.dayatang.product.domain.HtmlMail;
import com.dayatang.product.domain.InfoCategory;
import com.dayatang.product.domain.MailReceiver;
import com.dayatang.product.domain.MyAbstractEntity;
import com.dayatang.product.domain.Organization;
import com.dayatang.product.domain.Product;
import com.dayatang.product.domain.ProductMobileClient;
import com.dayatang.product.domain.ProductTrial;
import com.dayatang.product.domain.RegisterCode;
import com.dayatang.product.domain.RegisterInfo;
import com.dayatang.product.domain.SendMail;
import com.dayatang.product.domain.TrialCode;
import com.dayatang.product.domain.TrialStatus;
import com.dayatang.product.domain.Visit;
import com.dayatang.product.domain.VisitResult;
import com.dayatang.product.domain.VisitSubject;

public class ProductApplicationImpl implements ProductApplication {

	@Inject
	private EntityRepository repository;
	
	private EntityRepository getRepository() {
		if (repository == null) {
			repository = InstanceFactory.getInstance(EntityRepository.class);
		}
		return repository;
	}

	public void setRepository(EntityRepository repository) {
		this.repository = repository;
	}
	
	public <T extends MyAbstractEntity> T saveEntity(T entity) {
		T result = getRepository().save(entity);
		return result;
	}

	public void removeEntity(AbstractEntity entity) {
		entity.remove();
	}

	public void createProductTrial(ProductTrial productTrial, long trialPeriod) {
		
		Product product = Product.get(Product.class, productTrial.getProduct().getId());
		
		TrialCode trialCode = productTrial.getTrialCode();
		trialCode.setValidityDay(trialCode.getValidity());
		trialCode.setRegisterDate(new Date());
		trialCode.save();
		
		RegisterCode registerCode = new RegisterCode();
		registerCode.setRegisterCode(getMD5(productTrial.getTrialCode().getTrialNum().getBytes()));
		registerCode.setRegisterDate(new Date());
		if (trialPeriod > 0) {
			registerCode.setTrialDay(trialPeriod);
			productTrial.setTrialPeriod(trialPeriod);
		} else {
			registerCode.setTrialDay(product.getTrialPeriod());
			productTrial.setTrialPeriod(product.getTrialPeriod());
		}
		registerCode.save();
		
		productTrial.setRegisterCode(registerCode);
		productTrial.setStatus(TrialStatus.NEW);
		product.addProductTrial(productTrial);
		product.save();
	}

	public void obtainRegisterCode(TrialCode trialCode, RegisterInfo register) {
		register.save();
		trialCode.setActivateDate(new Date());
		trialCode.setActivate(true);
		trialCode.save();
		ProductTrial productTrial = ProductTrial.getProductTrialByTrialCode(trialCode);
		productTrial.setStatus(TrialStatus.ACTIVATE);
		productTrial.save();
	}
	
	public void activeRegisterCode(RegisterCode registerCode) {
		registerCode.setActivate(true);
		registerCode.setActivateDate(new Date());
		registerCode.save();
		ProductTrial productTrial = ProductTrial.getProductTrialByRegisterCode(registerCode);
		productTrial.setStatus(TrialStatus.USEING);
		productTrial.save();
	}

	public void updatePeriodDay() {
		for (TrialCode trialCode : TrialCode.getTrialCodeToPeriod()) {
			if (trialCode.getValidityDay() > 0) {
				if (trialCode.getValidityDay() == 1) {
					trialCode.setValidityDay(0);
					trialCode.setEffective(false);
					ProductTrial productTrial = ProductTrial.getProductTrialByTrialCode(trialCode);
					productTrial.setStatus(TrialStatus.DATED);
					productTrial.save();
				} else {
					trialCode.setValidityDay(trialCode.getValidityDay()-1);
				}
				trialCode.save();
			}
		}
	}

	public void updateTrialDay() {
		for (RegisterCode registerCode : RegisterCode.getRegisterCodeToTrial()) {
			if (registerCode.getTrialDay() > 0) {
				if (registerCode.getTrialDay() == 1) {
					registerCode.setTrialDay(0);
					registerCode.setOutDate(true);
					ProductTrial productTrial = ProductTrial.getProductTrialByRegisterCode(registerCode);
					productTrial.setStatus(TrialStatus.OVERTIME);
					productTrial.save();
				} else {
					registerCode.setTrialDay(registerCode.getTrialDay()-1);
				}
				registerCode.save();
			}
		}
	}

	public void saveSendMailInfo(HtmlMail htmlMail, List<Customer> customers) {
		SendMail sendMail = new SendMail();
		sendMail.setHtmlMail(htmlMail);
		for (Customer customer : customers) {
			MailReceiver mailReceiver = new MailReceiver();
			mailReceiver.setCustomer(customer);
			sendMail.addMailReceiver(mailReceiver);
			mailReceiver.setSendMail(sendMail);
		}
		sendMail.save();
	}

	public void removeProductTiral(ProductTrial productTrial) {
		productTrial.getRegisterCode().remove();
		productTrial.getTrialCode().remove();
		productTrial.remove();
	}

	public void removeHtmlMail(HtmlMail htmlMail) {
		for (Attachment attachment : htmlMail.getAttachments()) {
			File file = new File(attachment.getPath());
			if (file.exists()) {
				file.delete();
			}
		}
		htmlMail.remove();
	}

	public void removeSendMail(SendMail sendMail) {
		for (MailReceiver mailReceiver : sendMail.getMailReceivers()) {
			mailReceiver.remove();
		}
		sendMail.remove();
	}

	public void reportCustomerInfo(List<Object[]> objects) {
		for (Object[] object : objects) {
			CustomerInfo customerInfo = new CustomerInfo();
			customerInfo.setCompanyName(object[0] == null ? null : (String)object[0]);
			customerInfo.setPhone(object[1] == null ? null : (String)object[1]);
			customerInfo.setEmail(object[2] == null ? null : (String)object[2]);
			customerInfo.setProfessionType(object[3] == null ? null : String.valueOf(new Double((Double)object[3]).intValue()));
			customerInfo.setAddress(object[4] == null ? null : (String)object[4]);
			customerInfo.setCompanyUrl(object[5] == null ? null : (String)object[5]);
			customerInfo.setCustomerDescribe(object[6] == null ? null : (String)object[6]);
			customerInfo.setCategory(InfoCategory.CUSTOMERINFO);
			customerInfo.save();
		}
	}

	public void modifyTrialDay(ProductTrial productTrial, long trialDay) {
		RegisterCode registerCode = productTrial.getRegisterCode();
		if (!registerCode.getIsActivate()) {
			registerCode.setTrialDay(trialDay);
			registerCode.save();
			productTrial.setTrialPeriod(trialDay);
			productTrial.save();
		}
	}

	public void batchCreateProductTrial(Product product, long[] customers, long validity, long trialPeriod) {
		for (long customerId : customers) {
			product.addProductTrial(assemblyNewProductTrial(product,customerId,validity,trialPeriod));
		}
		product.save();
	}
	
	private ProductTrial assemblyNewProductTrial(Product product, long customerId, long validity, long trialPeriod) {
		
		ProductTrial productTrial = new ProductTrial();
		productTrial.setCustomerInfo(CustomerInfo.get(CustomerInfo.class, customerId));
		
		TrialCode trialCode = new TrialCode();
		trialCode.setTrialNum(createTrailCode(product.getMark()));
		trialCode.setValidity(validity);
		trialCode.setValidityDay(validity);
		trialCode.setRegisterDate(new Date());
		trialCode.save();
		
		productTrial.setTrialCode(trialCode);
		
		RegisterCode registerCode = new RegisterCode();
		registerCode.setRegisterCode(getMD5(trialCode.getTrialNum().getBytes()));
		registerCode.setRegisterDate(new Date());
		if (trialPeriod > 0) {
			registerCode.setTrialDay(trialPeriod);
			productTrial.setTrialPeriod(trialPeriod);
		} else {
			registerCode.setTrialDay(product.getTrialPeriod());
			productTrial.setTrialPeriod(product.getTrialPeriod());
		}
		registerCode.save();
		
		productTrial.setRegisterCode(registerCode);
		productTrial.setStatus(TrialStatus.NEW);
		
		return productTrial;
	}
	
	public String createTrailCode(String mark) {
		String trialCode = null;
		while(true) {
			trialCode = mark + randomNum();
			if (TrialCode.getTrialCodeByTrialCode(trialCode) == null) {
				break;
			}
		}
		return trialCode;
	}

	private String randomNum() {
		StringBuilder stringBuilder = new StringBuilder();
		for (int i = 0; i < 8; i++) {
			stringBuilder.append(new Random().nextInt(10));
		}
		return stringBuilder.toString();
	}
	
	public String getMD5(byte[] src) {
		StringBuffer sb = new StringBuffer();
		try {
			MessageDigest md = MessageDigest.getInstance("MD5");
			md.update(src);
			for (byte b : md.digest()) {
				sb.append(Integer.toString(b >>> 4 & 0xF, 16)).append(Integer.toString(b & 0xF, 16));
			}
		} catch (NoSuchAlgorithmException e) {
			e.toString();
		}
		sb.insert(new Random().nextInt(31), "a");
		sb.insert(new Random().nextInt(31), "b");
		return sb.toString();
	}

	public void updateMobileClient(ProductMobileClient productMobileClient, Set<Attachment> attachments) {
		for (Attachment attachment : productMobileClient.getAttachments()) {
			File file = new File(attachment.getPath());
			if (file.exists()) {
				file.delete();
			}
			attachment.remove();
		}
		productMobileClient.setAttachments(attachments);
		productMobileClient.save();
	}

	public void createLowerOrganization(Organization org, Organization parent) {
		int right = parent.getRightValue() - 1;
		List<Organization> organizations = getRepository().find("select o from Organization o where o.rightValue > ?", new Object[]{right}, Organization.class);
		for (Organization each : organizations) {
			each.setRightValue(each.getRightValue() + 2);
			each.save();
		}
		organizations = getRepository().find("select o from Organization o where o.leftValue > ?", new Object[]{right}, Organization.class);
		for (Organization each : organizations) {
			each.setLeftValue(each.getLeftValue() + 2);
			each.save();
		}
		org.setLeftValue(right + 1);
		org.setRightValue(right + 2);
		org.setLevel(parent.getLevel() + 1);
		org.save();
	}

	public void applyTrial(DownloadInfo downloadInfo, Product product) {
		downloadInfo.save();
		DownloadApproval approval = new DownloadApproval();
		approval.setDownloadInfo(downloadInfo);
		approval.setProduct(product);
		approval.setCategory(DownloadApprovalCategory.APPLY);
		approval.save();
	}

	public RegisterInfo approvalPass(DownloadApproval approval, long trialPeriod, long validity) {
		
		CustomerInfo customerInfo = new CustomerInfo();
		customerInfo.setCompanyName(approval.getDownloadInfo().getCompanyName());
		customerInfo.setAddress(approval.getDownloadInfo().getAddress());
		customerInfo.setEmail(approval.getDownloadInfo().getEmail());
		customerInfo.setPhone(approval.getDownloadInfo().getPhone());
		customerInfo.setCategory(InfoCategory.CUSTOMERINFO);
		customerInfo.save();
		
		Product product = approval.getProduct();
		ProductTrial productTrial = assemblyNewProductTrial(product, customerInfo.getId(), validity, trialPeriod);
		product.addProductTrial(productTrial);
		product.save();
		
		RegisterInfo register = new RegisterInfo();
		register.setCompanyName(approval.getDownloadInfo().getCompanyName());
		register.setPhone(approval.getDownloadInfo().getPhone());
		register.setEmail(approval.getDownloadInfo().getEmail());
		register.setContact(approval.getDownloadInfo().getContact());
		register.setAddress(approval.getDownloadInfo().getAddress());
		register.setMobile(approval.getDownloadInfo().getMobile());
		register.setCategory(InfoCategory.REGISTERINFO);
		register.setProductTrial(productTrial);
		register.save();
		
		obtainRegisterCode(productTrial.getTrialCode(),register);
		
		approval.setCategory(DownloadApprovalCategory.APPROVAL_PASS);
		approval.save();
		
		return register;
	}

	public CustomerVisit createCustomerVisit(String title, long[] trialIds, long[] subjectIds) {
		CustomerVisit customerVisit = new CustomerVisit();
		customerVisit.setTitle(title);
		customerVisit.setCreateDate(new Date());
		
		for (int i = 0; i < trialIds.length; i++) {
			Visit visit = new Visit();
			visit.setProductTrial(ProductTrial.get(ProductTrial.class, trialIds[i]));
			visit.setCustomerVisit(customerVisit);
			visit.setVisitSubject(VisitSubject.get(VisitSubject.class, subjectIds[i]));
			customerVisit.addVisit(visit);
		}
		customerVisit.save();
		return customerVisit;
	}

	public void recordVisitResult(VisitResult visitResult, long subjectId, long trialId) {
		visitResult.save();
		Visit visit = Visit.findByProductTrialAndVisitSubject(subjectId, trialId);
		if (visit != null) {
			visit.setVisitResult(visitResult);
			visit.save();
		}
	}

	public void saveVisitHelpAttachment(CustomerVisit customerVisit, Attachment attachment) {
		attachment.save();
		Set<Attachment> attachments = new HashSet<Attachment>();
		attachments.add(attachment);
		customerVisit.setAttachments(attachments);
		customerVisit.save();
	}

	public void checkActivateTrail() {
		List<ProductTrial> productTrials = ProductTrial.findByStatus(TrialStatus.ACTIVATE);
		for (ProductTrial productTrial : productTrials) {
			TrialCode code = productTrial.getTrialCode();
			code.setActivate(false);
			code.save();
		}
	}

}
